using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using Microsoft.Extensions.Logging;
using System;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using StackExchange.Redis;

namespace JGSY.CMS.LowCode.Platform.Infrastructure.Services
{
    /// <summary>
    /// Dragonfly 缓存服务实现
    /// 基于 Redis 协议的高性能缓存服务实现，使用 Dragonfly 作为缓存后端
    /// </summary>
    public class DragonflyCacheService : ICacheService
    {
        /// <summary>
        /// Redis 数据库连接实例
        /// </summary>
        private readonly IDatabase _db;
        
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<DragonflyCacheService> _logger;
        
        /// <summary>
        /// JSON 序列化配置选项
        /// </summary>
        private readonly JsonSerializerOptions _jsonOptions;

        /// <summary>
        /// 初始化 Dragonfly 缓存服务实例
        /// </summary>
        /// <param name="redis">Redis 连接复用器</param>
        /// <param name="logger">日志记录器</param>
        public DragonflyCacheService(IConnectionMultiplexer redis, ILogger<DragonflyCacheService> logger)
        {
            _db = redis.GetDatabase();
            _logger = logger;
            _jsonOptions = new JsonSerializerOptions 
            { 
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                PropertyNameCaseInsensitive = true,
                DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
            };
        }

        /// <summary>
        /// 异步设置缓存项
        /// 将键值对存储到 Dragonfly 缓存中，支持自定义过期时间
        /// </summary>
        /// <typeparam name="T">缓存值的类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <param name="value">缓存值</param>
        /// <param name="ttl">过期时间，默认30分钟</param>
        /// <returns>设置操作的异步任务</returns>
        /// <exception cref="Exception">当缓存操作失败时抛出</exception>
        public async Task SetAsync<T>(string key, T value, TimeSpan? ttl = null)
        {
            try
            {
                var json = JsonSerializer.Serialize(value, _jsonOptions);
                await _db.StringSetAsync(key, json, ttl ?? TimeSpan.FromMinutes(30));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Dragonfly SetAsync 操作失败: Key={Key}, Value={@Value}, TTL={TTL}", 
                    key, value, ttl);
                throw new Exception($"Dragonfly SetAsync error: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 异步获取缓存项
        /// 从 Dragonfly 缓存中检索指定键的值并反序列化
        /// </summary>
        /// <typeparam name="T">缓存值的类型</typeparam>
        /// <param name="key">缓存键</param>
        /// <returns>缓存值，如果不存在则返回默认值</returns>
        /// <exception cref="Exception">当缓存操作失败时抛出</exception>
        public async Task<T?> GetAsync<T>(string key)
        {
            try
            {
                var json = await _db.StringGetAsync(key);
                if (json.IsNullOrEmpty) return default;
                return JsonSerializer.Deserialize<T>(json!, _jsonOptions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Dragonfly GetAsync 操作失败: Key={Key}, Type={Type}", 
                    key, typeof(T).Name);
                throw new Exception($"Dragonfly GetAsync error: {ex.Message}", ex);
            }
        }

        /// <summary>
        /// 异步移除缓存项
        /// 从 Dragonfly 缓存中删除指定键的数据
        /// </summary>
        /// <param name="key">要移除的缓存键</param>
        /// <returns>移除操作的异步任务</returns>
        /// <exception cref="Exception">当缓存操作失败时抛出</exception>
        public async Task RemoveAsync(string key)
        {
            try
            {
                await _db.KeyDeleteAsync(key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Dragonfly RemoveAsync 操作失败: Key={Key}", key);
                throw new Exception($"Dragonfly RemoveAsync error: {ex.Message}", ex);
            }
        }
    }
}
